"""
AIV 模拟

采用 状态（State）设计模式
参考：https://refactoring.guru/design-patterns/state

"""

from common.global_var import *
from common import AIV


class VirtualAIV(AIV):
    """
        虚拟码头上的可运动的AIV，可以：
        - 接收并执行指令
        - 主动上报事件给码头
        对外接口
        - update 供外部调用，更新AIV状态
        - execute 执行传入的指令
        状态设计模式中的 Context
    """

    def __init__(self, properties: dict):

        super().__init__(properties)

        # 驾驶路径上的关键点位的坐标集合
        self.points = []
        # 每一步的点坐标
        self.steps = []
        # 走到 load 这一步要停下来装货
        self.load = None

        self.emit_event = None
        # 初始状态为 Free
        self.set_aiv_state(FreeAIV())

    def set_aiv_state(self, state_aiv):
        """
        改变当前状态
        """
        self.state_aiv = state_aiv
        self.state_aiv.set_aiv_context(self)

    @property
    def status(self):
        """
        以字符串类型获取当前集卡状态
        """
        return self.state_aiv.status

    def update(self, now, delta):
        """
        供外部调用，刷新AIV
        """
        self.state_aiv.update(now, delta)

    def execute(self, ins):
        """
        执行指令
        """
        if not ins:
            return None

        # 已经在动了，不用更新
        # if self._task_id == ins.task_id:
        #     return None

        self.state_aiv.execute(ins)

    def emit(self, event, params):
        """
        推送事件消息
        """
        if self.emit_event:
            self.emit_event(event, params)

    def set_position(self, point):
        """
        设置小车当前所在的点位
        """
        self._position = point

    def set_coordinate(self, coordinate):
        """
        更新小车当前坐标
        """
        self._x = coordinate['x']
        self._y = coordinate['y']

    def change_energy(self, delta):
        """
        更新小车当前的能源量（电量或油量）
        """
        self._battery += delta

    def set_task(self, ins):
        """
        为aiv设置任务和路径
        """
        self.steps = ins['points']
        self.points = ins['path']
        # self.load = ins['load']
        self._task_id = ins.task_id
    
    def set_departure(self, ins):
        """
        为aiv设置路径
        """
        self.steps = ins['points']
        self.points = ins['path']
        self._task_id = ins.signal

    def finish_task(self):
        """
        任务完成时被调用
        """
        # 能量检查，改变状态
        self.set_aiv_state(FreeAIV())
        
        # 请求单车调度
        self.emit('task-finished', {
            "aiv": self.info,
            "taskid": self._task_id,
            "signal": 'battery'
        })



class AIVState(object):
    """
    状态
    """

    def __init__(self, state):
        """
        初始化状态
        """
        self._state = state

    @property
    def status(self):
        """
        获取字符串格式的状态
        """
        return self._state.name

    def set_aiv_context(self, aiv_context):
        """
        设置 aiv 上下文
        """
        self.aiv = aiv_context

    def execute(self, instruction):
        """
        执行指令
        """
        pass

    def update(self, now, delta):
        """
        更新状态
        """
        pass


class FreeAIV(AIVState):
    """
    自由状态的小车
    """

    def __init__(self):
        """
        初始化状态
        """
        super().__init__(Status_AIV.Free)

        # 发车时间
        self.departure_time = None
        # 发车时需要提前的时间
        self.advance_time = 1

    def execute(self, instruction):
        """
        处于自由状态的小车，收到指令时执行的动作
        """
        # 加减速指令，啥都不做
        if instruction.ins_type == Type_Ins.Action:
            return

        # 立即发车指令
        if instruction.ins_type == Type_Ins.Departure:
            # 设置路径
            self.aiv.set_departure(instruction)
            # 更新状态
            self.aiv.set_aiv_state(MovingAIV())

        # 来任务了
        if instruction.ins_type == Type_Ins.Task:
            # 设置路径
            self.aiv.set_task(instruction)
            # 设置发车时间
            self.departure_time = instruction.time_start

    def update(self, now, delta):
        """
        等待发车
        """
        # 能量损耗
        self.aiv.change_energy(-Global.BatteryCostPerTick)

        if self.departure_time is None:
            return

        # 可以发车了
        if self.departure_time - now < self.advance_time:
            self.aiv.set_aiv_state(MovingAIV())


class WaitingAIV(AIVState):
    """
    等待状态的小车
    """

    def __init__(self):
        """
        初始化状态
        """
        super().__init__(Status_AIV.Waiting)

    def execute(self, instruction):
        """
        执行指令
        """
        pass

    def update(self, now, delta):
        """
        更新状态
        """
        pass


class MovingAIV(AIVState):
    """
    行驶状态的小车
    """

    def __init__(self):
        """
        初始化状态
        """
        super().__init__(Status_AIV.Moving)

        # 速度
        self.speed = 1
        # 当前的步骤
        self.current_step = 0

    def execute(self, instruction):
        """
        执行指令
        """
        if not instruction.ins_type == Type_Ins.Action:
            # 本不应该给运行状态的车辆分配任务或者下达发车指令
            print('[err]: virtualaiv.py, line 264')
            return
        
        # self.speed = 1

    def update(self, now, delta):
        """
        更新位置信息
        """
        # 获取当前点，把小数化整，用以支持小数的 speed
        current = int(self.current_step)

        # 如果走完了，说明当前任务结束
        if current > len(self.aiv.steps) - 1:
            self.aiv.set_coordinate(self.aiv.steps[-1])
            # self.aiv.set_position(self.aiv.points[-1])
            self.aiv.finish_task()
            return

        # 更新小车坐标
        coordinate = self.aiv.steps[current]
        self.aiv.set_coordinate(coordinate)
        # KTODO 更新小车的点位

        # 往前走 speed 步
        self.current_step += self.speed

        # 消耗能源
        self.aiv.change_energy(-Global.BatteryCostPerStep)
        

class ChargingAIV(AIVState):
    """
    充电状态的小车
    """

    def __init__(self):
        """
        初始化状态
        """
        super().__init__(Status_AIV.Charging)

    def execute(self, instruction):
        """
        执行指令
        """
        pass

    def update(self, now, delta):
        """
        更新状态
        """
        pass
